package xgin

import (
	"bytes"
	"encoding/csv"
	"fmt"

	"gitee.com/xiaoyutab/xgotool/optional/xlog"
	"gitee.com/xiaoyutab/xgotool/xstring"
)

// 列表结构体转CSV数据
//
//	head	CSV第一行的头信息
//	headkey	输出的Key下标顺序
//	csvbom	是否输出UTF8编码的BOM头【为了避免不同系统打开乱码】
//	body	列表形式的数组数据，如：[]model.xxxx
//	s_maps	MAP对应值,双维数组形式
//
// PS：因此操作使用的最多的为列表页面的导出功能，所以此处直接留存于xgin目录下，而非迁移到xstring下
func Struct2csv(head, headkey []string, csvbom bool, body any, s_maps map[string]map[string]string) ([]byte, error) {
	// 声明IO
	buffer := &bytes.Buffer{}
	writer := csv.NewWriter(buffer)
	if csvbom {
		// 写入UTF8声明的BOM头，避免excel打开乱码
		buffer.Write([]byte("\xEF\xBB\xBF"))
	}
	// 写入head
	err := writer.Write(head)
	if err != nil {
		return nil, xlog.EE("CSV表格的表头写入失败", err)
	}
	// 转化body为[]any
	temp := []any{}
	err = xstring.Any2any(body, &temp)
	if err != nil {
		return nil, xlog.EE("body转[]any失败", err)
	}
	for i := 0; i < len(temp); i++ {
		// any转map[string]any{}
		t_map := map[string]any{}
		err := xstring.Any2any(temp[i], &t_map)
		if err != nil {
			xlog.EE("[]any[i]转map[string]any失败", err)
			continue
		}
		// 进行数据输出
		write_csv(writer, headkey, t_map, s_maps)
	}
	writer.WriteAll([][]string{})
	return buffer.Bytes(), nil
}

// 写入表格数据
//
//	w	写入函数指针
//	l	写入顺序函数
//	b	写入参数
//	s	MAP对应值,双维数组形式
func write_csv(w *csv.Writer, l []string, b map[string]any, s map[string]map[string]string) {
	// 根据l的顺序获取b的数据
	tmp := []string{}
	for i := 0; i < len(l); i++ {
		temps := ""
		if v, ok := b[l[i]]; ok {
			// 判断类型进行写入
			switch t := v.(type) {
			case string:
				temps = t
			case float64:
				temps = fmt.Sprintf("%f", t)
			default:
				temps = fmt.Sprintf("暂不支持的类型：%T", v)
			}
		}
		tmp = append(tmp, switchMap(l[i], temps, s))
	}
	w.Write(tmp)
}

// 从map映射中获取
//
//	k	下标
//	v	查询到的值
//	s	MAP对应值,双维数组形式
func switchMap(k, v string, s map[string]map[string]string) string {
	if len(s) == 0 {
		return v
	}
	if m, ok := s[k]; ok {
		if t, ok := m[v]; ok {
			return t
		}
	}
	return v
}
